package com.clp.protocol.core.client;

import com.clp.protocol.core.utils.AssertUtil;
import com.clp.protocol.core.utils.CollectionUtil;

import javax.annotation.Nullable;
import java.util.*;
import java.util.concurrent.ScheduledExecutorService;
import java.util.stream.Collectors;

/**
 * 客户端事件 发布器
 */
public class ClientEventPublisher<C> implements ClientEventListenerRegistrar<C> {
    private final C client;
    private final ScheduledExecutorService executor;
    // Map<事件类型， 该事件的监听器列表>
    @SuppressWarnings("rawtypes")
    private final Map<Class<? extends ClientEvent>, List<ClientEventListener>> eventListenerMap = new HashMap<>();

    public ClientEventPublisher(C client, ScheduledExecutorService executor) {
        AssertUtil.nonNull(client);
        AssertUtil.nonNull(executor);
        this.client = client;
        this.executor = executor;
    }

    @SuppressWarnings({"rawtypes", "unchecked"})
    public <E extends ClientEvent<C>> void publishEvent(@Nullable E event) {
        AssertUtil.nonNull(event);
        synchronized (eventListenerMap) {
            List<ClientEventListener> listeners = eventListenerMap.get(event.getClass());
            if (listeners != null) {
                executor.execute(() -> {
                    try {
                        synchronized (eventListenerMap) {
                            listeners.forEach(listener -> listener.onEvent(event));
                        }
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                });
            }
        }
    }

    @Override
    @SuppressWarnings({"rawtypes"})
    public <E extends ClientEvent<C>> void addEventListener(@Nullable ClientEventListener<E> listener) {
        AssertUtil.nonNull(listener);

        Class<E> eventClass = listener.eventClass();
        synchronized (eventListenerMap) {
            List<ClientEventListener> listeners = eventListenerMap.computeIfAbsent(eventClass, k -> new ArrayList<>());
            listeners.add(listener);
        }
    }

    @Override
    @SuppressWarnings({"rawtypes"})
    public <E extends ClientEvent<C>> void addEventListeners(@Nullable Collection<ClientEventListener<E>> listeners) {
        if (CollectionUtil.isEmpty(listeners)) return;
        AssertUtil.nonNullIncludingElements(listeners);

        Class<E> eventClass = listeners.iterator().next().eventClass();
        synchronized (eventListenerMap) {
            List<ClientEventListener> inListeners = eventListenerMap.computeIfAbsent(eventClass, k -> new ArrayList<>());
            inListeners.addAll(listeners);
        }
    }

    @Override
    @SuppressWarnings({"rawtypes"})
    public <E extends ClientEvent<C>> void removeEventListener(@Nullable ClientEventListener<E> listener) {
        AssertUtil.nonNull(listener);

        Class<E> eventClass = listener.eventClass();
        synchronized (eventListenerMap) {
            List<ClientEventListener> listeners = eventListenerMap.computeIfAbsent(eventClass, k -> new ArrayList<>());
            listeners.remove(listener);
        }
    }

    @Override
    @SuppressWarnings({"rawtypes"})
    public <E extends ClientEvent<C>> void removeEventListeners(@Nullable Collection<ClientEventListener<E>> listeners) {
        if (CollectionUtil.isEmpty(listeners)) return;
        listeners = listeners.stream().filter(Objects::nonNull).collect(Collectors.toCollection(ArrayList::new));
        if (listeners.isEmpty()) return;

        Class<E> eventClass = listeners.iterator().next().eventClass();
        synchronized (eventListenerMap) {
            List<ClientEventListener> inListeners = eventListenerMap.computeIfAbsent(eventClass, k -> new ArrayList<>());
            inListeners.removeAll(listeners);
        }
    }
}
